use rust_decimal::Decimal;

use crate::calc::error::CalcResult;

mod ast;
mod error;
mod parser;
mod token;
mod tokenizer;

pub fn calculate(expression: &str) -> CalcResult<Decimal> {
    let mut parser = parser::Parser::new(expression)?;
    let ast = parser.parse()?;
    Ok(ast.eval())
}

#[cfg(test)]
mod tests {
    use rust_decimal::dec;

    use super::*;

    #[test]
    fn test_calculate() {
        assert_eq!(calculate("3 + 5").unwrap(), dec!(8));
        assert_eq!(calculate("10 - 2 * 3").unwrap(), dec!(4));
        assert_eq!(calculate("2 ^ 3 + 1").unwrap(), dec!(9));
        assert_eq!(calculate("15 / 3 - 1").unwrap(), dec!(4));
        assert_eq!(calculate("-(2 + 3)").unwrap(), dec!(-5));
        assert_eq!(calculate("42").unwrap(), dec!(42));
        assert_eq!(calculate("3.14 * 2").unwrap(), dec!(6.28));
        assert!(calculate("3 +").is_err());
        assert!(calculate("3 + 5 *").is_err());
        assert!(calculate("3 + (2 - 1").is_err());
        assert!(calculate("3 + (2 - 1))").is_err());
        assert!(calculate("3 + (2 - 1) *").is_err());
        assert!(calculate("3 + (2 - 1) * 4").is_ok());
    }
}
